package io;

import java.io.File; 
import java.io.IOException; 
import javax.sound.sampled.AudioFormat; 
import javax.sound.sampled.AudioInputStream; 
import javax.sound.sampled.AudioSystem; 
import javax.sound.sampled.DataLine; 
import javax.sound.sampled.FloatControl; 
import javax.sound.sampled.LineUnavailableException; 
import javax.sound.sampled.SourceDataLine; 
import javax.sound.sampled.UnsupportedAudioFileException; 

/**
 * A generic audio player based on javax.sound <br>
 * Responsible for playing sound effects.<br>
 * Currently supports 16-bit sounds only. 
 */
public class AudioPlay { 

	private boolean enabled = false;

	private static final int EXTERNAL_BUFFER_SIZE = 524288; // 128Kb 

	//Panning
	enum Position { 
		LEFT, RIGHT, NORMAL
	};

	/**
	 * Enables/Disables the audio player. It is disabled in its initial state,
	 * so this method has to be called once before any sound can be played.
	 * @param onOFF true -> enabled, false -> disabled
	 */
	public void setEnabled(Boolean onOFF) {
		enabled = onOFF;
	} 

	/**
	 * Creates a new thread that will play the specified sound.
	 * It is advised to use the sounds from Constants.Sounds
	 * @param file the path to the sound
	 * @see constants.Constants.Sounds
	 */
	public void playSound(String file){
		if(enabled)
			new Player(file).start();
	}

	//This is the thread that actually plays sounds
	private class Player extends Thread{

		private String filename;
		private Position curPosition;

		public Player(String file) {
			filename = file;
			curPosition = Position.NORMAL;
			setDaemon(true);
		} 

		public void run() { 

			File soundFile = new File(filename);
			
			if (!soundFile.exists()) { 
				System.err.println("Wave file not found: " + filename);
				return;
			} 

			//Get the data from the wave file
			AudioInputStream audioInputStream = null;			
			try { 
				audioInputStream = AudioSystem.getAudioInputStream(soundFile);
			} catch (UnsupportedAudioFileException e1) { 
				e1.printStackTrace();
				return;
			} catch (IOException e1) { 
				e1.printStackTrace();
				return;
			} 

			AudioFormat format = audioInputStream.getFormat();
			SourceDataLine auline = null;
			DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);

			//Open a line that will play our audio stream
			try { 
				auline = (SourceDataLine) AudioSystem.getLine(info);
				auline.open(format);
			} catch (LineUnavailableException e) { 
				e.printStackTrace();
				return;
			} catch (Exception e) { 
				e.printStackTrace();
				return;
			} 

			//Set the panning
			if (auline.isControlSupported(FloatControl.Type.PAN)) { 
				FloatControl pan = (FloatControl) auline
				.getControl(FloatControl.Type.PAN);
				if (curPosition == Position.RIGHT) 
					pan.setValue(1.0f);
				else if (curPosition == Position.LEFT) 
					pan.setValue(-1.0f);
			} 

			//Allow the line to process data
			auline.start();
			
			int nBytesRead = 0;
			byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];

			//Read all data from the input stream into the buffer and then to the line. 
			try { 
				while (nBytesRead != -1) { 
					nBytesRead = audioInputStream.read(abData, 0, abData.length);
					if (nBytesRead >= 0) 
						auline.write(abData, 0, nBytesRead);
				} 
			} catch (IOException e) { 
				e.printStackTrace();
				return;
			} finally { 
				auline.drain();
				auline.close();
			} 
		}
	}
}